package mm.util;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;

import net.sf.cglib.reflect.FastClass;

import mm.view.User;

public class ActionInvoker {

	private FastClass c;
	private int m;
	private Class[] parameterTypes;
	private Class returnType;
	private Object controller;
	private String mathodName; // only for printing
	
	private Class<? extends PushableUploadListener> uploadListenerClass;

	private boolean needingUser;
	private boolean needingId;
	private boolean needingSequence;
	private boolean needingParamMap;
	private boolean needingPusher;

	public ActionInvoker(Method method, Object controller) {
		Class clazz = method.getDeclaringClass();
		this.c = FastClass.create(clazz);
		this.parameterTypes = method.getParameterTypes();
		if(parameterTypes!=null&&parameterTypes.length>0){
			for (int i = 0; i < parameterTypes.length; i++) {
				Class parameterType = parameterTypes[i];
				if (parameterType.equals(User.class)){
					needingUser=true;
				}else if (parameterType.equals(String.class)){
					needingId=true;
				}else if (parameterType.equals(String[].class)){
					needingSequence=true;
				}else if (parameterType.equals(Map.class)){
					needingParamMap=true;
				}else if (parameterType.equals(Pusher.class)){
					needingPusher=true;
				}
			}
		}
		this.returnType=method.getReturnType();
		this.m = c.getIndex(method.getName(), parameterTypes );
		this.controller = controller;
		this.mathodName=method.getName();
		if (method.isAnnotationPresent(UploadListener.class)) {
			uploadListenerClass=method.getAnnotation(UploadListener.class).value();
			needingPusher=true;
		}
	}
	
	public Object invoke(User user, String id, String[] sequence, Map paramMap, Pusher pusher) throws Exception{
		if(parameterTypes==null||parameterTypes.length<1){
			return c.invoke(m, controller, null);
		}
		Object[] params=new Object[parameterTypes.length];
		for (int i = 0; i < params.length; i++) {
			Class paramClass = parameterTypes[i];
			if (paramClass.equals(User.class)){
				params[i]=user;
			}else if (paramClass.equals(String.class)){
				params[i]=id;
			}else if (paramClass.equals(String[].class)){
				params[i]=sequence;
			}else if (paramClass.equals(Map.class)){
				params[i]=paramMap;
			}else if (paramClass.equals(Pusher.class)){
				params[i]=pusher;
			}
		}
		return c.invoke(m, controller, params);
	}
	
	public Class getReturnType(){
		return returnType;
	}

	public PushableUploadListener getUploadListener() {
		try {
			return uploadListenerClass.newInstance();
		} catch (Exception e) {
			LOG.error("Can't instance listener without a default constructor", e);
			return null;
		}
	}

	@Override
	public String toString() {
		String s=returnType+" "+mathodName+"("+Arrays.toString(parameterTypes)+")";
		s=s.replace("[", "");
		s=s.replace("]", "");
		s=s.replaceAll("class\\sL([\\w\\.]+);", "$1[]");
		s=s.replaceAll("interface\\sL([\\w\\.]+);", "$1[]");
		s=s.replace("class ", "");
		s=s.replace("interface ", "");
		return s;
	}

	public boolean isNeedingUser() {
		return needingUser;
	}

	public boolean isNeedingId() {
		return needingId;
	}

	public boolean isNeedingSequence() {
		return needingSequence;
	}

	public boolean isNeedingParamMap() {
		return needingParamMap;
	}

	public boolean isNeedingPusher() {
		return needingPusher;
	}

}
